﻿using System;

namespace RealidadeAumentada.Entidades
{
    public class ELinhaVetorial2D
    {
        public double x;
        public double y;
        public double dx;
        public double dy;

        #region Setar Valor
        public void SetarValor(ELinhaVetorial2D i_value)
        {
            this.dx = i_value.dx;
            this.dy = i_value.dy;
            this.x = i_value.x;
            this.y = i_value.y;
        }
        #endregion

        #region Criar Array
        public static ELinhaVetorial2D[] CriarArray(int i_length)
        {
            ELinhaVetorial2D[] r = new ELinhaVetorial2D[i_length];
            for (int i = 0; i < i_length; i++)
            {
                r[i] = new ELinhaVetorial2D();
            }
            return r;
        }
        #endregion

        #region Normalizar Vetor
        public void NormalizarVetor(ELinhaVetorial2D i_src)
        {
            double w = this.dx;
            this.dx = i_src.dy;
            this.dy = -w;
        }
        #endregion

        #region Obter Coseno do Vetor
        public double ObterCosenoVetor(ELinhaVetorial2D i_v1)
        {
            double x1 = i_v1.dx;
            double y1 = i_v1.dy;
            double x2 = this.dx;
            double y2 = this.dy;
            double d = (x1 * x2 + y1 * y2) / Math.Sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2));
            return d;
        }

        public double ObterCosenoVetor(double i_dx, double i_dy)
        {
            double x1 = this.dx;
            double y1 = this.dy;
            double d = (x1 * i_dx + y1 * i_dy) / Math.Sqrt((x1 * x1 + y1 * y1) * (i_dx * i_dx + i_dy * i_dy));
            return d;
        }

        public double ObterCosenoVetor(EPontoDuplo2D i_pos1, EPontoDuplo2D i_pos2)
        {
            double d = ObterCosenoVetor(i_pos2.x - i_pos1.x, i_pos2.y - i_pos1.y);
            return d >= 0 ? d : -d;
        }
        #endregion

        #region Obter Coseno do Vetor Absisso
        public double ObterCosenoVetorAbsisso(ELinhaVetorial2D i_v1)
        {
            double x1 = i_v1.dx;
            double y1 = i_v1.dy;
            double x2 = this.dx;
            double y2 = this.dy;
            double d = (x1 * x2 + y1 * y2) / Math.Sqrt((x1 * x1 + y1 * y1) * (x2 * x2 + y2 * y2));
            return d >= 0 ? d : -d;
        }

        public double ObterCosenoVetorAbsisso(double i_v2_x, double i_v2_y)
        {
            double x1 = this.dx;
            double y1 = this.dy;
            double d = (x1 * i_v2_x + y1 * i_v2_y) / Math.Sqrt((x1 * x1 + y1 * y1) * (i_v2_x * i_v2_x + i_v2_y * i_v2_y));
            return d >= 0 ? d : -d;
        }

        public double ObterCosenoVetorAbsisso(EPontoDuplo2D i_pos1, EPontoDuplo2D i_pos2)
        {
            double d = ObterCosenoVetorAbsisso(i_pos2.x - i_pos1.x, i_pos2.y - i_pos1.y);
            return d >= 0 ? d : -d;
        }
        #endregion

        #region Posição em Cruz
        public bool crossPos(ELinhaVetorial2D i_vector1, EPontoDuplo2D o_point)
        {
            double a1 = i_vector1.dy;
            double b1 = -i_vector1.dx;
            double c1 = (i_vector1.dx * i_vector1.y - i_vector1.dy * i_vector1.x);
            double a2 = this.dy;
            double b2 = -this.dx;
            double c2 = (this.dx * this.y - this.dy * this.x);
            double w1 = a1 * b2 - a2 * b1;
            if (w1 == 0.0)
            {
                return false;
            }
            o_point.x = (b1 * c2 - b2 * c1) / w1;
            o_point.y = (a2 * c1 - a1 * c2) / w1;
            return true;
        }
        #endregion

        #region Distäncia do seguimento do quadrado a borda da linha
        public double DistanciaSeguimentoQuadradoBordaLinha(EPontoDuplo2D i_sp1, EPontoDuplo2D i_sp2)
        {
            double sa, sb, sc;
            sa = this.dy;
            sb = -this.dx;
            sc = (this.dx * this.y - this.dy * this.x);


            double lc;
            double x, y, w1;

            w1 = sa * (-sa) - sb * sb;
            if (w1 == 0.0)
            {
                return Double.PositiveInfinity;
            }

            lc = -(sb * i_sp1.x - sa * i_sp1.y);
            x = ((sb * lc + sa * sc) / w1) - i_sp1.x;
            y = ((sb * sc - sa * lc) / w1) - i_sp1.y;
            double sqdist = x * x + y * y;

            lc = -(sb * i_sp2.x - sa * i_sp2.y);
            x = ((sb * lc + sa * sc) / w1) - i_sp2.x;
            y = ((sb * sc - sa * lc) / w1) - i_sp2.y;

            return sqdist + x * x + y * y;
        }
        #endregion

        #region Setar Linha
        public bool setLinear(ELinha i_line, double i_x, double i_y)
        {
            double la = i_line.b;
            double lb = -i_line.a;
            double lc = -(la * i_x + lb * i_y);

            double w1 = -lb * lb - la * la;
            if (w1 == 0.0)
            {
                return false;
            }
            this.x = ((la * lc - lb * i_line.c) / w1);
            this.y = ((la * i_line.c + lb * lc) / w1);
            this.dy = -lb;
            this.dx = -la;
            return true;
        }
        #endregion

        #region Minimo Quadrados
        public bool MinimoQuadrados(EPontoDuplo2D[] i_points, int i_number_of_data)
        {
            int i;
            double sum_xy = 0, sum_x = 0, sum_y = 0, sum_x2 = 0;
            for (i = 0; i < i_number_of_data; i++)
            {
                EPontoDuplo2D ptr = i_points[i];
                double xw = ptr.x;
                sum_xy += xw * ptr.y;
                sum_x += xw;
                sum_y += ptr.y;
                sum_x2 += xw * xw;
            }
            double la = -(i_number_of_data * sum_x2 - sum_x * sum_x);
            double lb = -(i_number_of_data * sum_xy - sum_x * sum_y);
            double cc = (sum_x2 * sum_y - sum_xy * sum_x);
            double lc = -(la * sum_x + lb * sum_y) / i_number_of_data;

            double w1 = -lb * lb - la * la;
            if (w1 == 0.0)
            {
                return false;
            }
            this.x = ((la * lc - lb * cc) / w1);
            this.y = ((la * cc + lb * lc) / w1);
            this.dy = -lb;
            this.dx = -la;
            return true;
        }
        #endregion

        #region Minimo Quadrados Com Normalizacao
        public bool MinimoQuadradosComNormalizacao(EPontoDuplo2D[] i_points, int i_number_of_data)
        {
            bool ret = this.MinimoQuadrados(i_points, i_number_of_data);
            double sq = 1 / Math.Sqrt(this.dx * this.dx + this.dy * this.dy);
            this.dx *= sq;
            this.dy *= sq;
            return ret;
        }
        #endregion
    }
}
